package com.raymond.cmpp.client.netty;

import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSONObject;
import com.google.common.primitives.Bytes;
import com.raymond.cmpp.client.CmppClient;
import com.raymond.cmpp.client.msg.MessageManages;
import com.raymond.cmpp.common.constance.GlobalConstance;
import com.raymond.cmpp.common.msg.request.ConnectRequestMessage;
import com.raymond.cmpp.common.msg.response.ConnectResponseMessage;
import com.raymond.cmpp.common.utils.DateFormatUtils;
import com.raymond.cmpp.common.utils.SequenceIdUtil;
import io.netty.channel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * 连接处理
 *
 * @author :  raymond
 * @version :  V1.0
 * @date :  2021-08-06 11:39
 */
@ChannelHandler.Sharable
public class ReconnectHandler extends ChannelInboundHandlerAdapter {

    private final static Logger logger = LoggerFactory.getLogger(ReconnectHandler.class);
    /** 当前重试次数 **/
    private int retries = 0;
    /** 重连策略 **/
    private RetryPolicy retryPolicy;

    private CmppClient cmppClient;

    public ReconnectHandler(CmppClient cmppClient) {
        this.cmppClient = cmppClient;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("Successfully established a connection to the server.");
        retries = 0;
        doLogin();
        ctx.fireChannelActive();
    }

    private void doLogin() {

        // TODO 发送连接请求 ,创建密码
        ConnectRequestMessage req = new ConnectRequestMessage(SequenceIdUtil.getSequenceId());
        req.setSourceAddr(cmppClient.getSourceAddr());
        String timestamp = DateFormatUtils.getCmppData();
        req.setTimestamp(Integer.parseInt(timestamp));
        byte[] userBytes = cmppClient.getSourceAddr().getBytes(GlobalConstance.DEFAULT_TRANSPORT_CHARSET);
        byte[] pwdBytes = cmppClient.getPassword().getBytes(GlobalConstance.DEFAULT_TRANSPORT_CHARSET);
        byte[] timestampBytes = timestamp.getBytes(GlobalConstance.DEFAULT_TRANSPORT_CHARSET);
        req.setAuthenticatorSource(MD5.create().digest(Bytes.concat(userBytes, new byte[9], pwdBytes, timestampBytes)));
        cmppClient.setLoginStatus(-2);
        cmppClient.asyncSend(new MessageManages(req, messageManages ->{
            cmppClient.setLoginStatus(messageManages.getResp() == null ? -2 : (int) ((ConnectResponseMessage)messageManages.getResp()).getStatus());
            cmppClient.notifyAll0();
            logger.info("session Start :Send CmppConnectRequestMessage seq :{}, messageManages:{}", req.getSequenceId(), JSONObject.toJSONString(messageManages));
        }, 30000));


    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (cmppClient.isClose()) {
            logger.warn("连接已关闭");
            ctx.fireChannelInactive();
            return;
        }
        if (retries == 0) {
            System.err.println("Lost the TCP connection with the server.");
            ctx.close();
        }

        boolean allowRetry = getRetryPolicy().allowRetry(retries);
        if (allowRetry) {

            long sleepTimeMs = getRetryPolicy().getSleepTimeMs(retries);

            logger.warn(String.format("Try to reconnect to the server after %dms. Retry count: %d.", sleepTimeMs, ++retries));

            final EventLoop eventLoop = ctx.channel().eventLoop();
            eventLoop.schedule(() -> {
                logger.warn("Reconnecting ...");
                cmppClient.connect();
            }, sleepTimeMs, TimeUnit.MILLISECONDS);
        }
        ctx.fireChannelInactive();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 获取重连策略
     * @return 重连策略
     */
    private RetryPolicy getRetryPolicy() {
        if (this.retryPolicy == null) {
            this.retryPolicy = cmppClient.getRetryPolicy();
        }
        return this.retryPolicy;
    }
}
